/*-------------------------------------------------------------------------------------------------------------------------
 * Cube logic
 *
 * define cube related classes and made a logical cube structures.
 *
 * @author soonoh.jung@gmail.com
 * @since  April 2009
 *---------------------------------------------------------------------------------------------------------------------------*/
#ifndef CUBE_H_
#define CUBE_H_

#endif /*CUBE_H_*/

#include <iostream>
#include <stack>
#include <string>

using namespace std;

//#define CUBE_DEBUG

enum CUBE_FACES { CUBE_FACE_FRONT, CUBE_FACE_LEFT, CUBE_FACE_BACK, CUBE_FACE_RIGHT, CUBE_FACE_TOP, CUBE_FACE_BOTTOM };
typedef enum CUBE_FACES CUBE_FACES;
const int HIDDEN_FACE = 7;

// The direction of cube rotation.
enum CUBE_ROTARY_DIRECTION {
    CUBE_ROTARY_DIRECTION_POSITIVE,    // positive 90 dgree.
    CUBE_ROTARY_DIRECTION_OPPOSITE, // 180
    CUBE_ROTARY_DIRECTION_NEGATIVE  // -90
};
typedef enum CUBE_ROTARY_DIRECTION CUBE_ROTARY_DIRECTION;

// the Axises of Cube rotation.
enum CUBE_AXIS {
    CUBE_AXIS_X,
    CUBE_AXIS_Y,
    CUBE_AXIS_Z
};
typedef enum CUBE_AXIS CUBE_AXIS;

/** all faces of cube */
const int TOTAL_CUBE_FACE = 6;
/** size of cube, generally 2x2, 3x3, 4x4 and 5x5 cube played. 3x3 cube is most played in the world. */
const int BIG_CUBE_SIZE = 3;
/** a constant used evaluate coordinates or many other calculation. */
const int BIG_CUBE_LAST_INDEX = BIG_CUBE_SIZE - 1;
/** counts of small cubes in one rotation as same as count of cube in one face. */
const int ROTATE_CUBE_COUNT = BIG_CUBE_SIZE * BIG_CUBE_SIZE;
/** TODO: this can be moved other place, drawing cube width */
const int CUBE_WIDTH = 2;


/**-------------------------------------------------------------------------------
 * Cube
 * a piece of cube to consist of big cube can play game.
 * phisically cube get six faces but a piece of cube gets only one to tree faces
 * other faces are hidden. so we only consider visible 3 faces.
 *--------------------------------------------------------------------------------*/
class Cube {

private :

    int CubeColorArray[TOTAL_CUBE_FACE];

    void initCube( ) {
        this->initCubeColorArray();
    }

public :

    /**
     * creates a black cube, all faces is not coloured.
     */
    Cube( void  ) {
        this->initCube( );
    }

    /**
     * a copy of cube.
     */
    Cube( Cube &other ) {
        Cube::setColorArray( other.getCubeColorArray() );
    }

    /**-----------------------------------------------------------------------
     *  rotate based on X axis.
     *-----------------------------------------------------------------------*/
    void rotateX( int* copyCubeColorArray, CUBE_ROTARY_DIRECTION rotateTimes = CUBE_ROTARY_DIRECTION_POSITIVE ) {

        int i;
        for( i = 0; i < TOTAL_CUBE_FACE; ++i ) {
            copyCubeColorArray[i] = this->CubeColorArray[i];
        }

        if( rotateTimes == CUBE_ROTARY_DIRECTION_POSITIVE ) {
            copyCubeColorArray[CUBE_FACE_FRONT] = this->CubeColorArray[CUBE_FACE_BOTTOM];
            copyCubeColorArray[CUBE_FACE_TOP] = this->CubeColorArray[CUBE_FACE_FRONT];
            copyCubeColorArray[CUBE_FACE_BACK] = this->CubeColorArray[CUBE_FACE_TOP];
            copyCubeColorArray[CUBE_FACE_BOTTOM] = this->CubeColorArray[CUBE_FACE_BACK];
        } else if( rotateTimes == CUBE_ROTARY_DIRECTION_OPPOSITE ) {
            copyCubeColorArray[CUBE_FACE_FRONT] = this->CubeColorArray[CUBE_FACE_BACK];
            copyCubeColorArray[CUBE_FACE_TOP] = this->CubeColorArray[CUBE_FACE_BOTTOM];
            copyCubeColorArray[CUBE_FACE_BACK] = this->CubeColorArray[CUBE_FACE_FRONT];
            copyCubeColorArray[CUBE_FACE_BOTTOM] = this->CubeColorArray[CUBE_FACE_TOP];
        } else if( rotateTimes == CUBE_ROTARY_DIRECTION_NEGATIVE ) {
            copyCubeColorArray[CUBE_FACE_FRONT] = this->CubeColorArray[CUBE_FACE_TOP];
            copyCubeColorArray[CUBE_FACE_TOP] = this->CubeColorArray[CUBE_FACE_BACK];
            copyCubeColorArray[CUBE_FACE_BACK] = this->CubeColorArray[CUBE_FACE_BOTTOM];
            copyCubeColorArray[CUBE_FACE_BOTTOM] = this->CubeColorArray[CUBE_FACE_FRONT];
        }

    }

    /**-----------------------------------------------------------------------
     * rotate based on Y axis.
     *-----------------------------------------------------------------------*/
    void rotateY( int* copyCubeColorArray, CUBE_ROTARY_DIRECTION rotateTimes = CUBE_ROTARY_DIRECTION_POSITIVE ) {
        int i;
        for( i = 0; i < TOTAL_CUBE_FACE; ++i ) {
            copyCubeColorArray[i] = this->CubeColorArray[i];
        }

        if( rotateTimes == CUBE_ROTARY_DIRECTION_POSITIVE ) {
            copyCubeColorArray[CUBE_FACE_FRONT] = this->CubeColorArray[CUBE_FACE_RIGHT];
            copyCubeColorArray[CUBE_FACE_LEFT] = this->CubeColorArray[CUBE_FACE_FRONT];
            copyCubeColorArray[CUBE_FACE_BACK] = this->CubeColorArray[CUBE_FACE_LEFT];
            copyCubeColorArray[CUBE_FACE_RIGHT] = this->CubeColorArray[CUBE_FACE_BACK];
        } else if( rotateTimes == CUBE_ROTARY_DIRECTION_OPPOSITE ) {
            copyCubeColorArray[CUBE_FACE_FRONT] = this->CubeColorArray[CUBE_FACE_BACK];
            copyCubeColorArray[CUBE_FACE_LEFT] = this->CubeColorArray[CUBE_FACE_RIGHT];
            copyCubeColorArray[CUBE_FACE_BACK] = this->CubeColorArray[CUBE_FACE_FRONT];
            copyCubeColorArray[CUBE_FACE_RIGHT] = this->CubeColorArray[CUBE_FACE_LEFT];
        } else if( rotateTimes == CUBE_ROTARY_DIRECTION_NEGATIVE ) {
            copyCubeColorArray[CUBE_FACE_FRONT] = this->CubeColorArray[CUBE_FACE_LEFT];
            copyCubeColorArray[CUBE_FACE_LEFT] = this->CubeColorArray[CUBE_FACE_BACK];
            copyCubeColorArray[CUBE_FACE_BACK] = this->CubeColorArray[CUBE_FACE_RIGHT];
            copyCubeColorArray[CUBE_FACE_RIGHT] = this->CubeColorArray[CUBE_FACE_FRONT];
        }

    }

    /**
     * rotate based on Z axis.
     */
    void rotateZ(  int* copyCubeColorArray, CUBE_ROTARY_DIRECTION rotateTimes = CUBE_ROTARY_DIRECTION_POSITIVE ) {

        int i;
        for( i = 0; i < TOTAL_CUBE_FACE; ++i ) {
            copyCubeColorArray[i] = this->CubeColorArray[i];
        }

        if( rotateTimes == CUBE_ROTARY_DIRECTION_POSITIVE ) {
            copyCubeColorArray[CUBE_FACE_TOP] = this->CubeColorArray[CUBE_FACE_RIGHT];
            copyCubeColorArray[CUBE_FACE_LEFT] = this->CubeColorArray[CUBE_FACE_TOP];
            copyCubeColorArray[CUBE_FACE_BOTTOM] = this->CubeColorArray[CUBE_FACE_LEFT];
            copyCubeColorArray[CUBE_FACE_RIGHT] = this->CubeColorArray[CUBE_FACE_BOTTOM];
        } else if( rotateTimes == CUBE_ROTARY_DIRECTION_OPPOSITE ) {
            copyCubeColorArray[CUBE_FACE_TOP] = this->CubeColorArray[CUBE_FACE_BOTTOM];
            copyCubeColorArray[CUBE_FACE_LEFT] = this->CubeColorArray[CUBE_FACE_RIGHT];
            copyCubeColorArray[CUBE_FACE_BOTTOM] = this->CubeColorArray[CUBE_FACE_TOP];
            copyCubeColorArray[CUBE_FACE_RIGHT] = this->CubeColorArray[CUBE_FACE_LEFT];
        } else if( rotateTimes == CUBE_ROTARY_DIRECTION_NEGATIVE ) {
            copyCubeColorArray[CUBE_FACE_TOP] = this->CubeColorArray[CUBE_FACE_LEFT];
            copyCubeColorArray[CUBE_FACE_LEFT] = this->CubeColorArray[CUBE_FACE_BOTTOM];
            copyCubeColorArray[CUBE_FACE_BOTTOM] = this->CubeColorArray[CUBE_FACE_RIGHT];
            copyCubeColorArray[CUBE_FACE_RIGHT] = this->CubeColorArray[CUBE_FACE_TOP];
        }
    }


    void initCubeColorArray() {
        int i;
        for( i = 0; i < TOTAL_CUBE_FACE; ++i ) {
            this->CubeColorArray[i] = HIDDEN_FACE;
        }
    }

    void setColorArray( int* colorArray ) {
        int i;
        for( i = 0; i < TOTAL_CUBE_FACE; ++i ) {
            this->CubeColorArray[i] = colorArray[i];
        }
    }

    int* getCubeColorArray() {
        return this->CubeColorArray;
    }


/////////////// End Cube ///////////////////////////////
};

/**-------------------------------------------------------------------------------
 * A cash cubes, to cash rotated cubes states.
 *--------------------------------------------------------------------------------*/
class CashCube {

    int CashCubeX;
    int CashCubeY;
    int CashCubeZ;
    int* CashCubeColor;

public :

    CashCube() {
        this->initCashCube( 0, 0, 0 );
        this->CashCubeColor = new int[TOTAL_CUBE_FACE];
    }

    CashCube( int x, int y, int z, int* colorArray) {
        this->initCashCube( x, y, z, colorArray );
    }

    int getX(){
        return this->CashCubeX;
    }

    int getY() {
        return this->CashCubeY;
    }
    int getZ() {
        return this->CashCubeZ;
    }
    int* getColor() {
        return this->CashCubeColor;
    }

    void initCashCube( int x, int y, int z ) {
        this->CashCubeX = x;
        this->CashCubeY = y;
        this->CashCubeZ = z;
    }

    void initCashCube( int x, int y, int z, int* colorArray ) {
        this->CashCubeX = x;
        this->CashCubeY = y;
        this->CashCubeZ = z;
        this->CashCubeColor = colorArray;
    }
//////////////////// End CashCube /////////////////////////////
};

/**--------------------------------------------------------------------------------------------------------------------------------------------------
 * a cube is consisted of pieces of cubes, each AXIS is sized of BIG_CUBE_AXIS.
 *-----------------------------------------------------------------------------------------------------------------------------------------------------*/
class BigCube {

private :

    /**
     * the index of cube to cash currently, when we rotate cubes in one face, each cubes to be gonna rotate
     * is rotated respectably and saved in cash first. after finished all rotation of each cubes, finally propagate
     * cubes rotates states to cubes in BigCube.
     */
    int currentCashIndex;

    /** each cubes in this BigCube. */
    Cube* BigCubeChildren[BIG_CUBE_SIZE][BIG_CUBE_SIZE][BIG_CUBE_SIZE];

    /** a cash cubes to be rotated. */
    CashCube* BigCubeCashCube[ROTATE_CUBE_COUNT];

public :

    /**
     * Constructor, build small cubes and cash cubes. actually only faces cubes used.
     */
    BigCube( void  ) {
        currentCashIndex = 0;
        int x, y, z;
        for( x = 0; x < BIG_CUBE_SIZE; ++x ) {
            for( y = 0; y < BIG_CUBE_SIZE; ++y ) {
                for( z = 0; z < BIG_CUBE_SIZE; ++z ) {
                    if ( (x > 0 && x < BIG_CUBE_LAST_INDEX) && (y > 0 && y < BIG_CUBE_LAST_INDEX) && (z > 0 && z < BIG_CUBE_LAST_INDEX) ) {
                        // core cube can be removed.
                        BigCubeChildren[x][y][z] = NULL;
                        continue;
                    }
                    Cube* cube = new Cube();
                    BigCubeChildren[x][y][z] = cube;

                    // now each face to proper colour(to set groups or to set faces)
                    if (x == 0) {
                        cube->getCubeColorArray()[CUBE_FACE_LEFT] = CUBE_FACE_LEFT;
                    }
                    if (x == BIG_CUBE_LAST_INDEX) {
                        cube->getCubeColorArray()[CUBE_FACE_RIGHT] = CUBE_FACE_RIGHT;
                    }
                    if (y == 0) {
                        cube->getCubeColorArray()[CUBE_FACE_BOTTOM] = CUBE_FACE_BOTTOM;
                    }
                    if (y == BIG_CUBE_LAST_INDEX) {
                        cube->getCubeColorArray()[CUBE_FACE_TOP] = CUBE_FACE_TOP;
                    }
                    if (z == 0) {
                        cube->getCubeColorArray()[CUBE_FACE_FRONT] = CUBE_FACE_FRONT;
                    }
                    if (z == BIG_CUBE_LAST_INDEX) {
                        cube->getCubeColorArray()[CUBE_FACE_BACK] = CUBE_FACE_BACK;
                    }
                }
            }
        }
        for( x = 0; x < ROTATE_CUBE_COUNT; ++ x ) {
            this->BigCubeCashCube[x] = new CashCube();
        }
    }


    /**
     * propagate cubes states to real cubes which is rotated.
     */
    void syncCube() {
        int i;
        int x, y, z;
        for( i = 0; i < currentCashIndex; ++i ) {
            x = this->BigCubeCashCube[i]->getX();
            y = this->BigCubeCashCube[i]->getY();
            z = this->BigCubeCashCube[i]->getZ();
            Cube* cube = this->BigCubeChildren[x][y][z];
            if (cube != NULL) {
                cube->setColorArray( this->BigCubeCashCube[i]->getColor() );
            }
        }
        currentCashIndex = 0;
    }

    /**
     * rotate cubes based on X axis in this BigCube.
     */
    void rotateX( int x, CUBE_ROTARY_DIRECTION rotateTimes ) {

        int y, z;

#ifdef CUBE_DEBUG
        cout << "X row " << x <<  " column ↑" << rotateTimes << endl;
#endif

        for( y = 0; y < BIG_CUBE_SIZE; ++y ) {
            for( z = 0; z < BIG_CUBE_SIZE; ++z  ) {
                this->rotateXCube( x, y, z, rotateTimes );
            }
        }
        this->syncCube();
#ifdef CUBE_DEBUG
        this->debugShowColor();
#endif

    }

    /**
     * rotate cubes based on Y axis in this BigCube.
     */
    void rotateY( int y, CUBE_ROTARY_DIRECTION rotateTimes ) {

        int x, z;

#ifdef CUBE_DEBUG
        cout << "Y row " << y <<  " column ->" << rotateTimes << endl;
#endif

        for( x = 0; x < BIG_CUBE_SIZE; ++x ) {
            for( z = 0; z < BIG_CUBE_SIZE; ++z  ) {
                this->rotateYCube( x, y, z, rotateTimes  );
            }
        }
        this->syncCube();
#ifdef CUBE_DEBUG
        this->debugShowColor();
#endif
    }

    /**
     * rotate cubes based on Z axis in this BigCube.
     */
    void rotateZ( int z, CUBE_ROTARY_DIRECTION rotateTimes ) {

        int x, y;

#ifdef CUBE_DEBUG
        cout << "Z row" << z <<  " column ->" << rotateTimes << endl;
#endif

        for( x = 0; x < BIG_CUBE_SIZE; ++x ) {
            for( y = 0; y < BIG_CUBE_SIZE; ++y  ) {
                this->rotateZCube( x, y, z, rotateTimes  );
            }
        }
        this->syncCube();
#ifdef CUBE_DEBUG
        this->debugShowColor();
#endif
    }

    /**
     * rotate one cube located in x,y,z.
     * the rotation makes cube move another location in BigCube, and the faces are changed.
     * to complish one whole rotation, use cash for each small cube rotation.
     -*/
    void rotateXCube( int x, int y, int z, CUBE_ROTARY_DIRECTION rotateTimes ) {
        // get moved coordinates. Y coordinates not changed in X axis rotation.
        int rx, ry, rz;
        rx = x; ry = y; rz = z;
        if( rotateTimes == CUBE_ROTARY_DIRECTION_POSITIVE ) {
            ry = BIG_CUBE_LAST_INDEX - z;
            rz = y;
        } else if( rotateTimes == CUBE_ROTARY_DIRECTION_OPPOSITE  ) {
            ry = BIG_CUBE_LAST_INDEX - y;
            rz = BIG_CUBE_LAST_INDEX - z;
        } else if( rotateTimes == CUBE_ROTARY_DIRECTION_NEGATIVE  ) {
            ry = z;
            rz = BIG_CUBE_LAST_INDEX - y;
        }

        // each cube rotates and set rotated colours.
        if (this->BigCubeChildren[x][y][z] != NULL)
        {
            this->BigCubeChildren[x][y][z]->rotateX( this->BigCubeCashCube[currentCashIndex]->getColor(), rotateTimes );
            this->setCashCube( rx, ry, rz );
        }

    }

    /**
     * rotate one cube located in x,y,z based on Y axis.
     */
    void rotateYCube( int x, int y, int z, CUBE_ROTARY_DIRECTION rotateTimes) {

        int rx, ry, rz;
        rx = x; ry = y; rz = z;
        if( rotateTimes == CUBE_ROTARY_DIRECTION_POSITIVE ) {
            rx = z;
            rz = BIG_CUBE_LAST_INDEX - x;
        } else if( rotateTimes == CUBE_ROTARY_DIRECTION_OPPOSITE  ) {
            rx = BIG_CUBE_LAST_INDEX - x;
            rz = BIG_CUBE_LAST_INDEX - z;
        } else if( rotateTimes == CUBE_ROTARY_DIRECTION_NEGATIVE  ) {
            rx = BIG_CUBE_LAST_INDEX - z;
            rz = x;
        }

        if (this->BigCubeChildren[x][y][z] != NULL)
        {
            this->BigCubeChildren[x][y][z]->rotateY( this->BigCubeCashCube[currentCashIndex]->getColor(), rotateTimes  );
            this->setCashCube( rx, ry, rz );
        }

    }

    /**
     * rotate one cube located in x,y,z based on Z axis.
     */
    void rotateZCube( int x, int y, int z, CUBE_ROTARY_DIRECTION rotateTimes) {

        int rx, ry, rz;
        rx = x; ry = y; rz = z;
        if( rotateTimes == CUBE_ROTARY_DIRECTION_POSITIVE ) {
            rx = BIG_CUBE_LAST_INDEX - y;
            ry = x;
        } else if( rotateTimes == CUBE_ROTARY_DIRECTION_OPPOSITE  ) {
            rx = BIG_CUBE_LAST_INDEX - x;
            ry = BIG_CUBE_LAST_INDEX - y;
        } else if( rotateTimes == CUBE_ROTARY_DIRECTION_NEGATIVE  ) {
            rx = y;
            ry = BIG_CUBE_LAST_INDEX - x;
        }

        if (this->BigCubeChildren[x][y][z] != NULL)
        {
            this->BigCubeChildren[x][y][z]->rotateZ( this->BigCubeCashCube[currentCashIndex]->getColor(), rotateTimes  );
            this->setCashCube( rx, ry, rz );
        }

    }

    /**
     * create cash cube located in x,y,z.
     */
    void setCashCube( int x, int y, int z ) {
        this->BigCubeCashCube[currentCashIndex]->initCashCube( x, y, z );
        ++currentCashIndex;
        if( currentCashIndex > ROTATE_CUBE_COUNT ) {
            this->syncCube();
        }
    }

    void setCube( Cube* cube, int x, int y, int z ) {

        this->BigCubeChildren[ x ][ y ][ z ] = cube;

    }

    Cube* getCube( int x, int y, int z ) {
        return this->BigCubeChildren[ x ][ y ][ z ];
    }

    bool isPerfectCube() {
        int x, y, z;
        int left, right, top, bottom, front, back;
        left = right = top = bottom = front = back = HIDDEN_FACE;
        for( x = 0; x < BIG_CUBE_SIZE; ++x ) {
            for( y = 0; y < BIG_CUBE_SIZE; ++y ) {
                for( z = 0; z < BIG_CUBE_SIZE; ++z ) {
                    Cube* cube = BigCubeChildren[x][y][z];
                    if (cube == NULL) continue;

                    // now each face to proper colour(to set groups or to set faces)
                    if (x == 0) {
                        if (left == HIDDEN_FACE) {
                            left = cube->getCubeColorArray()[CUBE_FACE_LEFT];
                        } else {
                            if (cube->getCubeColorArray()[CUBE_FACE_LEFT] != left) return false;
                        }
                    }
                    if (x == BIG_CUBE_LAST_INDEX) {
                        if (right == HIDDEN_FACE) {
                            right = cube->getCubeColorArray()[CUBE_FACE_RIGHT];
                        } else {
                            if (cube->getCubeColorArray()[CUBE_FACE_RIGHT] != right) return false;
                        }
                    }
                    if (y == 0) {
                        if (bottom == HIDDEN_FACE) {
                            bottom = cube->getCubeColorArray()[CUBE_FACE_BOTTOM];
                        } else {
                            if (cube->getCubeColorArray()[CUBE_FACE_BOTTOM] != bottom) return false;
                        }
                    }
                    if (y == BIG_CUBE_LAST_INDEX) {
                        if (top == HIDDEN_FACE) {
                            top = cube->getCubeColorArray()[CUBE_FACE_TOP];
                        } else {
                            if (cube->getCubeColorArray()[CUBE_FACE_TOP] != top) return false;
                        }
                    }
                    if (z == 0) {
                        if (front == HIDDEN_FACE) {
                            front = cube->getCubeColorArray()[CUBE_FACE_FRONT];
                        } else {
                            if (cube->getCubeColorArray()[CUBE_FACE_FRONT] != front) return false;
                        }
                    }
                    if (z == BIG_CUBE_LAST_INDEX) {
                        if (back == HIDDEN_FACE) {
                            back = cube->getCubeColorArray()[CUBE_FACE_BACK];
                        } else {
                            if (cube->getCubeColorArray()[CUBE_FACE_BACK] != back) return false;
                        }
                    }
                }
            }
        }
        return true;
    }
#ifdef CUBE_DEBUG
/*===========[debug functions]======================*/

    /**
     * debug function to show all faces in console.
     */
    void debugShowColor() {
        int y;
        cout << "Left\t| " << "Front\t| " << "Right\t| " << "Back\t| " << "Top\t| " << "Bottom\t| " << endl;
        for( y = 2; y > -1 ; -- y ) {
            debugShowLine( 0, y, 0, 'z', 1 );
            debugShowLine( y, 0, 1, 'x', 0 );
            debugShowLine( 2, y, 1, 'z', 3 );
            debugShowLine( y, 2, 0, 'x', 2 );
            //cout << endl;
            debugShowLine( 2, y, 1, 'x', 4 );
            debugShowLine( 0, 2 - y , 1, 'x', 5 );
            cout << endl;
        }

    }

    void debugShowLine( int initFirst, int initSecond, int isASC, char changeableValType, int face ) {
        Cube* cube;
        int changeableVal = 0;
        if( changeableValType == 'x' ) {
            if( isASC == 0 ) {
                for( changeableVal = 2; changeableVal > -1; -- changeableVal ) {
                        cube = BigCubeChildren[changeableVal][initFirst][initSecond];
                        cout << colorToChar( cube->getCubeColorArray()[face] );
                }
            } else {
                for( changeableVal = 0; changeableVal < 3; ++ changeableVal ) {
                        cube = BigCubeChildren[changeableVal][initFirst][initSecond];
                        cout << colorToChar( cube->getCubeColorArray()[face] );
                }
            }

        } else if( changeableValType == 'y' ) {
            if( isASC == 0 ) {
                for( changeableVal = 2; changeableVal > -1; -- changeableVal ) {
                        cube = BigCubeChildren[initFirst][changeableVal][initSecond];
                        cout << colorToChar( cube->getCubeColorArray()[face] );
                }
            } else {
                for( changeableVal = 0; changeableVal < 3; ++ changeableVal ) {
                        cube = BigCubeChildren[initFirst][changeableVal][initSecond];
                        cout << colorToChar( cube->getCubeColorArray()[face] );
                }
            }

        } else if( changeableValType == 'z' ) {
            if( isASC == 0 ) {
                for( changeableVal = 2; changeableVal > -1; -- changeableVal ) {
                        cube = BigCubeChildren[initFirst][initSecond][changeableVal];
                        cout << colorToChar( cube->getCubeColorArray()[face] );
                }
            } else {
                for( changeableVal = 0; changeableVal < 3; ++ changeableVal ) {
                        cube = BigCubeChildren[initFirst][initSecond][changeableVal];
                        cout << colorToChar( cube->getCubeColorArray()[face] );
                }
            }

        }

        cout << "| ";

    }

    char* colorToChar( int c ) {
        return colorToFigure( c );
    }

    char* colorToString( int c ) {
        if( c == 0 ) {
            return "Red ";
        } else if( c == 1 ) {
            return "White ";
        } else if( c == 2 ) {
            return "Green ";
        } else if( c == 3 ) {
            return "Yellow ";
        } else if( c == 4 ) {
            return "Blue ";
        } else if( c == 5 ) {
            return "Orange ";
        }
    }

    char* colorToFigure( int c ) {
        if( c == 0 ) {
            return "R ";
        } else if( c == 1 ) {
            return "W ";
        } else if( c == 2 ) {
            return "G ";
        } else if( c == 3 ) {
            return "Y ";
        } else if( c == 4 ) {
            return "B ";
        } else if( c == 5 ) {
            return "O ";
        }
        return 0;
    }

    void toString() {
        Cube* cube;
        int x, y, z;
        for( x = 0; x < BIG_CUBE_SIZE; ++x ) {
            for( y = 0; y < BIG_CUBE_SIZE; ++y ) {
                for( z = 0; z < BIG_CUBE_SIZE; ++z ) {
                    cube = BigCubeChildren[x][y][z];
                    cout << "X= " << toStringX( x )
                        << "Y= " << toStringY( y )
                        << "Z= " << toStringZ( z )<< endl;
                }
            }
        }

    }

    char* toStringX( int x ) {
        if( x == 0 ) {
            return "Left ";
        } else if( x == 1  ) {
            return "X Middle ";
        } else if( x == 2  ) {
            return "Right ";
        }
    }
    char* toStringY( int y ) {
        if( y == 0 ) {
            return "Bottom ";
        } else if( y == 1  ) {
            return "Y Middle ";
        } else if( y == 2  ) {
            return "Top ";
        }
    }
    char* toStringZ( int z ) {
        if( z == 0 ) {
            return "Front ";
        } else if( z == 1  ) {
            return "Z Middle ";
        } else if( z == 2  ) {
            return "Back ";
        }
    }
#endif
///////////////////// End BigCube ////////////////////////////
};



class CubeRotateOperation {

private :

    CUBE_AXIS axis;
    int column;
    CUBE_ROTARY_DIRECTION direction;
    /** there is two way to rotate interfaces, first is using mouse other press key
     * which is represented by the faces of view(eye) point
     * if eye point based rotation needs to convert axis of rotation and other information.
     * therefore we need what kind of this operation.
     * */
    bool isRotateBasedOnEyePoint;

public :

    CubeRotateOperation() {
        CubeRotateOperation( CUBE_AXIS_X, 0, CUBE_ROTARY_DIRECTION_POSITIVE );
    }

    CubeRotateOperation( char rotateChar, bool isReverse=false ) {
        int reverse = 1;
        if( isReverse ) {
            reverse = -1;
        }
        switch( rotateChar ) {
            case 'r': setRotateOperation( 0, BIG_CUBE_LAST_INDEX, reverse * 1 ); break;
            case 'R': setRotateOperation( 0, BIG_CUBE_LAST_INDEX, reverse * -1 ); break;
            case 'l': setRotateOperation( 0, 0, reverse * -1 ); break;
            case 'L': setRotateOperation( 0, 0, reverse * 1 ); break;
            case 'u': setRotateOperation( 1, BIG_CUBE_LAST_INDEX, reverse * 1 ); break;
			case 'U': setRotateOperation( 1, BIG_CUBE_LAST_INDEX, reverse * -1 ); break;
			case 'd': setRotateOperation( 1, 0, reverse * -1 ); break;
			case 'D': setRotateOperation( 1, 0, reverse * 1 ); break;
			case 'f': setRotateOperation( 2, BIG_CUBE_LAST_INDEX, reverse * -1 ); break;
			case 'F': setRotateOperation( 2, BIG_CUBE_LAST_INDEX, reverse * 1 ); break;
			case 'b': setRotateOperation( 2, 0, reverse * 1 ); break;
			case 'B': setRotateOperation( 2, 0, reverse * -1 ); break;
			default :
			break;
		}
		isRotateBasedOnEyePoint = true;
	}

	CubeRotateOperation( int axisInt, int column, int directionInt ){
		setRotateOperation( axisInt, column, directionInt);
		isRotateBasedOnEyePoint = false;
	}
	CubeRotateOperation( CUBE_AXIS axis, int column, CUBE_ROTARY_DIRECTION direction ) {
		setRotateOperationInner( axis, column, direction);
		isRotateBasedOnEyePoint = false;
	}

	bool IsRotateBasedOnEyePoint() {
		return isRotateBasedOnEyePoint;
	}

	void setRotateOperation( int axisInt, int column, int directionInt ){
		CUBE_AXIS axis;
		CUBE_ROTARY_DIRECTION direction;
		switch( axisInt ) {
			case 0: axis = CUBE_AXIS_X;
				direction = (directionInt == 1) ? CUBE_ROTARY_DIRECTION_NEGATIVE : CUBE_ROTARY_DIRECTION_POSITIVE;
				break;
			case 1: axis = CUBE_AXIS_Y;
				direction = (directionInt == 1) ? CUBE_ROTARY_DIRECTION_NEGATIVE : CUBE_ROTARY_DIRECTION_POSITIVE;
				break;
			case 2: axis = CUBE_AXIS_Z;
				direction = (directionInt == 1) ? CUBE_ROTARY_DIRECTION_POSITIVE : CUBE_ROTARY_DIRECTION_NEGATIVE;
				break;
		}
		setRotateOperationInner( axis, column, direction);

	}

	void setRotateOperationInner( CUBE_AXIS axis, int column, CUBE_ROTARY_DIRECTION direction ) {
		this->axis = axis;
		this->column = column;
		this->direction = direction;
	}

	int getAxisInteger() {
		return int(axis);
	}

	CUBE_AXIS getAxis() {
		return axis;
	}

	int getColumn() {
		return column;
	}

	int getColumnReverse() {
		return BIG_CUBE_LAST_INDEX - column;
	}

	int getDirectionInteger() {
		int direction;
		switch( this->axis ) {
			case CUBE_AXIS_X: this->axis = CUBE_AXIS_X;
				direction = (this->direction == CUBE_ROTARY_DIRECTION_NEGATIVE) ? 1:-1;
				break;
			case CUBE_AXIS_Y: this->axis = CUBE_AXIS_Y;
				direction = (this->direction == CUBE_ROTARY_DIRECTION_NEGATIVE) ? 1:-1;
				break;
			case CUBE_AXIS_Z: this->axis = CUBE_AXIS_Z;
				direction = (this->direction == CUBE_ROTARY_DIRECTION_NEGATIVE) ? -1:1;
				break;
		}
		return direction;
	}

	CUBE_ROTARY_DIRECTION getDirection() {
		return direction;
	}

	static void initRandom() {
		srand( time(NULL) );
	}

	/**
	 * get random rotation.
	 */
	static CubeRotateOperation* random() {
		int i = rand() % 3;
		CubeRotateOperation* cubeRotateOperation = new CubeRotateOperation();
		switch( i ) {
			case 0: cubeRotateOperation->axis = CUBE_AXIS_X; break;
			case 1: cubeRotateOperation->axis = CUBE_AXIS_Y; break;
			case 2: cubeRotateOperation->axis = CUBE_AXIS_Z; break;
		}
		cubeRotateOperation->column = rand() % BIG_CUBE_SIZE;
		cubeRotateOperation->direction = (rand() % 2 == 0) ? CUBE_ROTARY_DIRECTION_NEGATIVE : CUBE_ROTARY_DIRECTION_POSITIVE;
		return cubeRotateOperation;
	}
};

stack<CubeRotateOperation*> cubeRotateOperationStack;

//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
// F2L formula, TODO: this gonna be removed.

string F2L[] = {
	"fRFr"
	,"FlfL" //1B
	,"rruufrrFuuRuR" //2
	,"rruuRUrURuuR" //4A
	,"lluuluLuluul" //4B
	,"rUrubUBrr"
	,"LuLUBubll" //5B
	,"urUbuuBuR"
	,"rURFuuf"
	,"BdbUBDb"
	,"bDBubdB" //8b
	,"UruRuFUf"
	,"uLUlUfuf"//9b
	,"UFufurUR"
	,"ufUFULul"//10b
	,"rUrrfrF"
	,"LullFLf"//11b
	,"ruRUruR"
	,"LUluLUl"//12b
	,"uFUf"
	,"UfuF"//13b
	,"ruubUBuR"
	,"LuuBubUl"//14b
	,"FLulfruR"
	,"frURFLUl"//15b
	,"FuufuFUf"
	,"fuuFUfuF"//16b
	,"FuLuuluuf"
	,"fUruuRuuF"//17b
	,"FLBublf"
	,"frbUBRF"//18b
	,"uFufUFUf"
	,"UfUFufuF"//19b
	,"FlFLff"
	,"fRfrff"//20b
	,"FuuffRFr"
	,"fuufflfL"//21B
	,"UFUfUFUf"
	,"ufuFufuF"//22b
	,"lFRfrfL"
	,"RflFLFr" //23b
};

int F2L_SIZE = 41;
int F2L_index = 0;

/*
void main(void) {
	BigCube* bigCube = new BigCube();

	bigCube->debugShowColor();

	bigCube->rotateY( 2, 0 );
	bigCube->rotateX( 2, 0 );
	bigCube->rotateY( 0, 0 );
	bigCube->rotateX( 0, 0 );

	bigCube->rotateX( 0, 2 );
	bigCube->rotateY( 0, 2 );
	bigCube->rotateX( 2, 2 );
	bigCube->rotateY( 2, 2 );

}
*/
